<html>
  <head>
    <title>对状态进行保留和重置</title>
  </head>
  <body>
    <script>
      // 对状态进行保留重置
            /*
                各个组件的状态是各自独立的。
                根据组件在UI树中的位置，React可以跟踪哪些状态属于哪个组件。
                你可以控制在重新渲染过程中何时对状态进行"保留"和"重置"
            */

            // 一、“状态”与渲染树中的位置相关
                    /*
                        React会为UI中的组件结构构建"渲染树"。

                        当向一个组件添加状态时，那么可能会认为状态"存在"在组件中。但实际上，状态是由React保存的。
                        React通过"组件在渲染树中的位置"将它保存的"每个状态"与"正确的组件"关联起来。
                    */

                       // 下面的例子中只有一个<Counter/> JSX标签，但它会在连个不同的位置渲染：
                              import {useSate} from 'react';
                              export default function App(){
                                const counter =<Counter/>;

                                return (
                                  <div>
                                    {counter}
                                    {counter}
                                  </div>
                                );
                              }

                              function Counter(){
                                const [score,setScore] =useSate(0);
                                const [hover,setHover] =useSate(false);

                                let className='counter';

                                if(hover){
                                  className+=' hover';
                                }
                              }

                      /*
                          这是两个独立的Counter，因为他们自爱树中被渲染在了各自的位置。
                          一般情况下你不用去考虑这些来使用React，但是知道它们是如何工作的很有用。

                          在React中，屏幕中的每个组件都有完全独立的状态。
                          举个例子，当你并排渲染两个Counter时，它们都会拥有各自的 "score" 和 "hover"状态变量。

                          试试点击这两个 counter 你会发现它们互不影响。

                          只有当在树中相同位置渲染相同的组件时，React才会一直保留着组件的状态。
                          想要验证这一点，可以将两个技术器的值递增，取消勾选"渲染第二个计数器"复选框，然后再次勾选它。

                          注意：当你停止渲染第二个计数器的那一刻，它的状态完全消失了。这是因为React在移除一个组件时，也会销毁它的状态。

                          当你重新勾选"渲染第二个计数器"复选框时，另一个计数器极其状态将从头开始初始化(score=0)并被添加到ＤＯＭ中

                          所以
                              "只要一个组件还被渲染在UI树的相同位置，React就会保留它的状态"，
                              如果它被移除，或者一个不同的组件被渲染在相同的位置，那么React就会丢弃它的状态。
                      */



            // 二、相同位置的相同组件会使得状态被保留下来
                      // 在这个例子中，有两个不同的<Counter/>标签：
                              import {useSate} from 'react';

                              function Counter({isFancy}){
                                const [score,setScore]=useSate(0);
                                const [hover,setHover]=useSate(false);

                                let className='counter';
                                if(hover){
                                  className +=' hover';
                                }
                                if(isFancy){
                                  className +=' fancy';
                                }
                              }

                              export default function App(){
                                const [isFancy,setIsFancy] = useSate(false);
                                return (
                                    <div>
                                        {
                                          isFancy?(
                                            <Counter isFancy={true}/>  // 这个组件与下面的<Counter/> 虽然写在JSX的不同位置，但是在UI树中是相同位置，又是相同组件，所以React认为它们是"同一组件"
                                          ):(
                                            <Counter isFancy={false}/> // 
                                          )
                                        }
                                        <label>
                                            <input 
                                                type="checkbox"
                                                checked={isFancy}
                                                onChange={e=>{
                                                  setIsFancy(e.target.checked);
                                                }}
                                            />
                                            使用更好看的样式
                                        </label>
                                    </div>
                                );
                              }

                          /*
                              当你勾选或清空复选框时，计数器状态并没有被重置。
                              不管isFancy的值如何，根组件App 返回的div的第一个子组件都是<Counter/>

                              它是位于"相同位置"的"相同组件"，所以对React来说，它是同一个组件。
                          */

                          /*
                              注意：
                                  对React来说重要的是组件在UI树中的位置，而不是在JSX中的位置！
                                  这个组件在if内外有两个return语句，它们带有不同的<Counter /> JSX标签

                                  你可能以为当你勾选复选框的时候状态会被重置，但它没有！ 
                                  这是因为两个<Counter />标签被渲染在了相同的位置。
                                  React不知道你的函数里是如何进行条件判断的，它只"看到"你返回的树。在这两种情况下 App组件都返回一个包裹着<Counter/>
                                  作为第一个子组件的Div, 这就是React认为它们是"同一个<Counter/>组件"的原因。
                          */ 


            // 三、相同位置渲染不同组件会是状态重置
                          // 在下面的例子里，勾选复选框会将<Counter/> 替换为一个<p>:
                                import {useSate} from 'react';

                                function Counter(){
                                  const [score,setScore]=useSate(0);
                                  const [hover,setHover] =useSate(false);

                                  let className='counter ';
                                  if(hover){
                                    className+=' hover';
                                  }

                                  return (
                                      <div 
                                          className={className}
                                          onPointerEnter={()=>setHover(true)}
                                          onPointerLeave={()=>setHover[false]}
                                      >
                                          <h1>{score}</h1>
                                          <button onClick={()=>setScore(score+1)}>
                                              加一
                                          </button>
                                      </div>

                                  );
                                }

                                export default function App(){
                                    const [isPaused,setIsPaused] =useSate(false);

                                    return (
                                      <div>
                                          {
                                            isPaused?(
                                              <p>待会见！</p>
                                            ):(
                                              <Counter />
                                            )
                                          }
                                          <label>
                                              <input 
                                                  type="checkbox"
                                                  checked={isPaused}
                                                  onChange={(e)=>{
                                                    setIsPaused(e.target.checked);
                                                  }} 
                                                />
                                                休息一下
                                          </label>
                                      </div>
                                    );
                                }

                            /*
                                示例中，你在相同位置对不同的组件进行切换。
                                刚开始<div>的第一个子组件是一个<Counter/> 。但是，当你切换成<p>时，React将Counter从UI树中移除了并销毁了它的状态。
                            */
                            // 并且，当你在相同位置渲染不同的组件时，组件的整个子树都会被重置。

                            /*
                               一般来说，如果你想在重新渲染时保留状态，几次渲染中的树形结构就应该相互"匹配"。
                               结构不同就会导致状态的销毁，因为React会在将一个组件从树中移除时销毁它的状态。、


                               注意：
                                    组件函数的定义，千万不能嵌套。
                            */

            // 四、在相同的位置重置状态
                          /*
                              默认情况下，React会在一个组件保持在同一位置时保留它的状态。
                              通常这就是你要想的，所以把它作为默认特性很合理。
                              但有时候，你可能想要重置一个组件的状态
                          */
                                  import {useState} from 'react';
                                  function Counter({person}){
                                        const [score,setScore] =useSate(0);
                                        const [hover,setHover] =useSate(false);

                                        let className='counter';
                                        if(hover){
                                          className+=' hover';
                                        }
                                        return (
                                          <div
                                              className={className}
                                              onPointerEnter={()=>setHover(true)}
                                              onPointerLeave={()=>setHover(false)}
                                          >
                                            <h1>{person}的分数</h1>
                                            <button onClick={()=>setScore(score+1)}>\
                                                加一
                                            </button> 
                                          </div>
                                        );
                                  }

                                  export default function Scoreboard(){
                                        const [isPlayerA,setIsPlayerA] =useSate(true);

                                        return (
                                            <div>
                                                {isPlayerA?(
                                                    <Counter person="Taylor"/>
                                                ):(
                                                    <Counter person="Sarah"/>
                                                )}

                                                <button onClick={()=>{
                                                    setIsPlayerA(!isPlayerA);
                                                }}>
                                                  下一位玩家！
                                                </button>
                                            </div>

                                        );
                                  }

                          /*
                              目前当你切换玩家时，分数会被保留下来。
                              这两个Counter出现在相同的位置，所以React会认为它们是同一个Counter，只是传递了不认同的person属性。

                              但是从概念上讲，这个应用的两个计数器应该是各自独立的。
                              虽然它们在UI中的位置相同，但是一个是Taylor的计数器，一个是Sarah的计数器。
                          */

                          /*
                              有两个方法可以在它们相互切换时重置状态：
                                  1、将组件渲染在不同的位置
                                  2、使用key赋予每个组件一个明确的身份：
                          */

                                  // 1、方法一： 将组件渲染在不同的位置：
                                           // 如果你想让两个Counter各自独立的话，可以将它们渲染在不同的位置
                                            import {useState} from 'react';
                                            function Counter({person}){
                                                  const [score,setScore] =useSate(0);
                                                  const [hover,setHover] =useSate(false);

                                                  let className='counter';
                                                  if(hover){
                                                    className+=' hover';
                                                  }
                                                  return (
                                                    <div
                                                        className={className}
                                                        onPointerEnter={()=>setHover(true)}
                                                        onPointerLeave={()=>setHover(false)}
                                                    >
                                                      <h1>{person}的分数</h1>
                                                      <button onClick={()=>setScore(score+1)}>\
                                                          加一
                                                      </button> 
                                                    </div>
                                                  );
                                            }

                                            export default function Scoreboard(){
                                                  const [isPlayerA,setIsPlayerA] =useSate(true);
                                                  return (
                                                    <div>
                                                         {
                                                            isPlayerA && <Counter person="Taylor"/>
                                                         }
                                                         {
                                                            !isPlayerA && <Counter person="Sarah"/>
                                                         }
                                                         <button onClick={()=>{
                                                              setIsPlayerA(!isPlayerA);
                                                         }}>
                                                             下一位玩家！
                                                         </button>
                                                    </div>
                                                  );
                                            }

                                            /*
                                                起初 isPlayerA 的值是true。所以第一个位置包含了Counter的状态，而第二个位置是空的。
                                                当你点击"下一位玩家！"按钮时，第一个位置会被清空，而在第二个位置现在包含了一个Counter 。

                                                每当Counter组件从DOM中移除时，它的state会被销毁。这就是每次点击按钮它们就会被重置的原因。

                                                这个解决方案在你只有少数几个独立组件渲染在相同的位置时会很方便。
                                            */


                                  // 2、方法二：使用key来重置state
                                            /*
                                                还有另一种更通用的重置组件状态的方法。

                                                你可能在"渲染列表"时见到过key。
                                                但可以不只可以用于列表！ 你可以使用key来让React区分任何组件。
                                                默认情况下，React使用父组件内部的顺序("第一个组件"、"第二个组件")来区分组件。
                                                但是可以可以让你告诉React这不仅仅是"第一个"或者"第二个"计数器，而且还是一个特定的计数器。
                                            */

                                             // 在这个例子中，即时两个<Counter />会出现在JSX中的同一个位置，它们也不会共享状态。
                                                import {useState} from 'react';
                                                function Counter({person}){
                                                      const [score,setScore] =useSate(0);
                                                      const [hover,setHover] =useSate(false);

                                                      let className='counter';
                                                      if(hover){
                                                        className+=' hover';
                                                      }
                                                      return (
                                                        <div
                                                            className={className}
                                                            onPointerEnter={()=>setHover(true)}
                                                            onPointerLeave={()=>setHover(false)}
                                                        >
                                                          <h1>{person}的分数</h1>
                                                          <button onClick={()=>setScore(score+1)}>\
                                                              加一
                                                          </button> 
                                                        </div>
                                                      );
                                                }

                                                export default function Scoreboard(){
                                                     const [isPlayerA,setIsPlayerA] =useSate(true);

                                                     return (
                                                        {
                                                          isPlayerA ? (
                                                            <Counter key="Taylor" person="Taylor"/>
                                                          ):(
                                                            <Counter key="Sarah" person="Sarah"/>
                                                          )
                                                        }
                                                        <button onClick={()=>{
                                                            setIsPlayerA(!isPlayerA);
                                                        }}>
                                                          下一位玩家！
                                                        </button>
                                                     );
                                                }

                                            /*
                                                在Taylor和 Sarah之间切换不会使状态被保留下来。因为"你给他们赋予了不同的key"
                                            */ 

                                           /*
                                                指定一个key能够让React将key 本身而非它们在父组件中的顺序作为位置的一部分。
                                                这就是为什么尽管你用JSX将组件渲染在相同的位置，但在React看来它们是两个不同的计数器。
                                                一次它们永远都不会共享状态。


                                                注意：key 不是全局唯一的。它们只能指定父组件内部的顺序。
                                           */


                                    // 3、使用key重置表单
                                            /*
                                                使用key来重置状态在处理表单时特别有用。
                                            */

                                            
                                  
    </script>
  </body>
</html>